package com.wyz.socket.core.base;

import com.wyz.socket.core.base.exceptions.IoArgsReadFromException;
import com.wyz.socket.core.base.exceptions.IoArgsWriteToException;

import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;

// 对缓存的封装
public class IoArgs {

    private volatile int limit = 256;
    private final boolean isNeedConsumeRemaining;
    private final ByteBuffer buffer;

    public IoArgs() {
        this(256);
    }

    public IoArgs(int limit) {
        this(limit, true);
    }

    public IoArgs(int limit, boolean isNeedConsumeRemaining) {
        this.limit = limit;
        this.isNeedConsumeRemaining = isNeedConsumeRemaining;
        this.buffer = ByteBuffer.allocate(limit);
    }

    public int readFrom(byte[] header, int offset, int count) {
        int size = Math.min(buffer.remaining(), count);
        if (size <= 0) {
            return 0;
        }
        buffer.put(header, offset, size);
        return size;
    }

    public int writeTo(byte[] header, int offset, int count) {
        int size = Math.min(buffer.remaining(), count);
        if (size <= 0) {
            return 0;
        }
        buffer.get(header, offset, size);
        return size;
    }


    public int readFrom(ReadableByteChannel channel) throws IOException {
        int byteCount = 0;
        while (buffer.hasRemaining()) {
            int len = channel.read(buffer);
            if (len < 0) {
               throw new IoArgsReadFromException("Ioargs readfrom ReadableByteChannel length("+len+") < 0 ") ;
                // break;
            }
            byteCount += len;
        }
        return byteCount;
    }

    public int writeTo(WritableByteChannel channel) throws IOException {
        int byteCount = 0;
        while (buffer.hasRemaining()) {
            int len = channel.write(buffer);
            if (len < 0) {
               throw new IoArgsWriteToException("Ioargs writeTo WritableByteChannel length("+len+") < 0 ") ;
                //  break;
            }
            byteCount += len;
        }
        return byteCount;
    }

    public int readFrom(SocketChannel channel) throws IOException {
        ByteBuffer buffer = this.buffer;
        int readByteCount = 0;
        int len;
        do {
            len = channel.read(buffer);
            if (len < 0) {
                throw new IoArgsReadFromException("Ioargs readfrom SocketChannel length("+len+") < 0 ") ;
                //  break ;
            }
            readByteCount += len;
        } while (len != 0 && buffer.hasRemaining());

        return readByteCount;
    }

    public int writeTo(SocketChannel channel) throws IOException {
        ByteBuffer buffer = this.buffer;
        int writeByteCount = 0;
        int len;
        do {
            len = channel.write(this.buffer);
            if (len < 0) {
               throw new IoArgsWriteToException("Ioargs writeTo SocketChannel length("+len+") < 0 ") ;
                //  break ;
            }
            writeByteCount += len;
        } while (buffer.hasRemaining() && len != 0);
        return writeByteCount;
    }

    public void startWriting() {
        buffer.clear();
        buffer.limit(limit);
    }

    public void limit(int limit) {
        this.limit = Math.min(limit, capacity());
    }

    public int getLimit() {
        return limit;
    }

    public void endWriting() {
        buffer.flip();
    }

    public int capacity() {
        return buffer.capacity();
    }

    public int remaining() {
        return buffer.remaining();
    }

    public boolean hasRemaining() {
        return buffer.hasRemaining();
    }

    public int fillEmpty(int size) {
        int fillSize = Math.min(size, remaining());
        buffer.position(buffer.position() + fillSize);
        return fillSize;
    }

    public int setEmpty(int size) {
        int fillSize = Math.min(size, remaining());
        buffer.position(buffer.position() + fillSize);
        return fillSize;
    }

    public void resetLimit() {
        this.limit = buffer.capacity();
    }

    public boolean isNeedConsumeRemaining() {
        return isNeedConsumeRemaining;
    }


    public interface IoBufferEventProcessor {
        void onstart();

        IoArgs providerIoArgs();

        /**
         * 消费失败时回调
         * @param e 异常信息
         * @return 是否关闭连接 True 关闭
         */
        boolean onConsumeFailed(Throwable e);

        /**
         * 消费完成的回调
         * @param args 待消费的Ioargs
         * @return True 在此将自身注册,原因时还需要在此消费
         */
        boolean onConsumeCompleted(IoArgs args);

        void onEnd();
    }
}
